﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using BlueCMS.Core.Data;
using BlueCMS.Core.Data.Query.Clauses;
using BlueCMS.Core.Data.Query.Columns;
using BlueCMS.Core.Data.Query.Common;
using BlueCMS.Core.Data.Query.Tables;
using BlueCMS.Core.Providers;

namespace BlueCMS.Core.Data.Query.Generator
{
    public abstract class QueryGeneratorBase
    {
        public virtual string ParameterToken
        {
            get { throw new NotImplementedException(); }
        }

        public virtual string StartingQuoteToken
        {
            get { throw new NotImplementedException(); }
        }

        public virtual string EndingQuoteToken
        {
            get { throw new NotImplementedException(); }
        }

        public virtual string ProcessSelectQuery(SelectClause select, FromClause from, WhereClause where, GroupByClause groupBy, HavingClause having, OrderByClause orderBy, bool distinct, int limit, int offset)
        {
            StringBuilder sb = new StringBuilder();
            GenerateSelectStatement(select, distinct, offset, limit, sb);
            GenerateFromStatement(from, sb);
            //where = string.Empty;
            GenerateGroupByStatement(groupBy, sb);
            //having = string.Empty;
            GenerateOrderByStatement(orderBy, sb);

            return sb.ToString();
        }

        protected virtual void GenerateSelectStatement(SelectClause clause, bool distinct, int limit, int offset, StringBuilder sb)
        {
            sb.Append("SELECT ");

            if (distinct)
            {
                sb.Append("DISTINCT ");
            }

            if (clause.SelectColumns.Count == 0)
            {
                sb.Append("*");
            }
            else
            {
                foreach (SelectColumn column in clause.SelectColumns)
                {
                    GenerateSelectColumn(column, sb);
                    sb.Append(", ");
                }
                sb.Remove(sb.Length - 2, 2);
            }
        }

        protected virtual void GenerateFromStatement(FromClause clause, StringBuilder sb)
        {
            if (clause.Tables.Count == 0)
            {
                throw new QueryException("Invalid from clause, must specify at least one table");
            }

            sb.Append(" FROM ");

            foreach (Table table in clause.Tables)
            {
                if (table.JoinedTo == null)
                {
                    sb.Append(GenerateTableName(table));
                    sb.Append(", ");
                }
                else
                {
                    sb.Append(GenerateJoinStatement(table));
                    sb.Append(", ");
                }
            }
            sb.Remove(sb.Length - 2, 2);
        }

        protected virtual string GenerateJoinStatement(Table table)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(GenerateTableName(table));
            bool first = true;

            for(JoinTable jt = table.JoinedTo; jt != null; jt = jt.JoinedTo)
            {
                if (!first)
                {
                    sb.Insert(0, "(");
                    sb.Append(")");
                }
                sb.Append(" ");
                sb.Append(GenerateJoinName(jt.Type));
                sb.Append(" ");
                sb.Append(GenerateTableName(jt));
                sb.Append(" ON 1 = 1");
                first = false;
            }

            return sb.ToString();
        }

        protected virtual void GenerateSelectColumn(SelectColumn column, StringBuilder sb)
        {
            GenerateColumn((Column)column, sb);
            if (!string.IsNullOrEmpty(column.Alias))
            {
                sb.Append(" AS ");
                GenerateAliasName(column.Alias, sb);
            }
        }

        protected virtual void GenerateColumn(Column column, StringBuilder sb)
        {
            if (column.NameOrExpression is SelectQuery)
            {
                sb.Append("(");
                sb.Append(column.NameOrExpression.ToString());
                sb.Append(")");
            }
            else
            {
                sb.Append(column.NameOrExpression.ToString());
            }
        }

        protected virtual void GenerateAliasName(string alias, StringBuilder sb)
        {
            sb.Append("'");
            sb.Append(alias);
            sb.Append("'");
        }

        protected virtual string GenerateTableName(Table table)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(StartingQuoteToken);
            sb.Append(table.Name);
            sb.Append(EndingQuoteToken);
            if (!string.IsNullOrEmpty(table.Alias))
            {
                sb.Append(" AS ");
                sb.Append(StartingQuoteToken);
                sb.Append(table.Alias);
                sb.Append(EndingQuoteToken);
            }
            return sb.ToString();
        }

        protected virtual string GenerateJoinName(JoinType type)
        {
            switch (type)
            {
                case JoinType.InnerJoin:
                    return "JOIN";
                case JoinType.CrossJoin:
                    return "CROSS JOIN";
                case JoinType.RightOuterJoin:
                    return "RIGHT JOIN";
                case JoinType.LeftOuterJoin:
                    return "LEFT JOIN";
                case JoinType.FullOuterJoin:
                    return "FULL JOIN";
                default:
                    throw new QueryException("Unknown join type");
            }
        }

        protected virtual void GenerateOrderByStatement(OrderByClause clause, StringBuilder sb)
        {
            if (clause.OrderByColumns.Count == 0)
            {
                return;
            }

            sb.Append(" ORDER BY ");

            foreach (OrderByColumn column in clause.OrderByColumns)
            {
                GenerateColumn(column, sb);
                if (column.SortDirection == SortDirection.Descending)
                {
                    sb.Append(" DESC");
                }
                sb.Append(", ");
            }
            sb.Remove(sb.Length - 2, 2);
        }

        protected virtual void GenerateGroupByStatement(GroupByClause clause, StringBuilder sb)
        {
            if (clause.Columns.Count == 0)
            {
                return;
            }

            sb.Append(" GROUP BY ");

            foreach (Column column in clause.Columns)
            {
                GenerateColumn(column, sb);
                sb.Append(", ");
            }
            sb.Remove(sb.Length - 2, 2);
        }

        //protected virtual List<string> SqlFunctions
        //{
        //    get
        //    {
        //        if (sqlFunctions == null)
        //        {
        //            sqlFunctions = new List<string>();

        //            // Aggregate Functions
        //            sqlFunctions.Add("AVG");
        //            sqlFunctions.Add("COUNT");
        //            sqlFunctions.Add("COUNT");
        //            sqlFunctions.Add("MIN");
        //            sqlFunctions.Add("MAX");
        //            sqlFunctions.Add("SUM");
        //            sqlFunctions.Add("CHECKSUM");
        //            sqlFunctions.Add("CHECKSUM_AGG");
        //            sqlFunctions.Add("STDEV");
        //            sqlFunctions.Add("STDEVP");
        //            sqlFunctions.Add("COUNT_BIG");
        //            sqlFunctions.Add("GROUPING");
        //            sqlFunctions.Add("VAR");
        //            sqlFunctions.Add("VARP");

        //            // Date and Time Functions
        //            sqlFunctions.Add("DATEADD");
        //            sqlFunctions.Add("DATEDIFF");
        //            sqlFunctions.Add("DATENAME");
        //            sqlFunctions.Add("DATEPART");
        //            sqlFunctions.Add("DAY");
        //            sqlFunctions.Add("GETDATE");
        //            sqlFunctions.Add("GETUTCDATE");
        //            sqlFunctions.Add("MONTH");
        //            sqlFunctions.Add("YEAR");

        //            // Math Functions
        //            sqlFunctions.Add("ABS");
        //            sqlFunctions.Add("ACOS");
        //            sqlFunctions.Add("ASIN");
        //            sqlFunctions.Add("ATAN");
        //            sqlFunctions.Add("ATN2");
        //            sqlFunctions.Add("CEILING");
        //            sqlFunctions.Add("COS");
        //            sqlFunctions.Add("COT");
        //            sqlFunctions.Add("DEGREES");
        //            sqlFunctions.Add("EXP");
        //            sqlFunctions.Add("FLOOR");
        //            sqlFunctions.Add("LOG");
        //            sqlFunctions.Add("LOG10");
        //            sqlFunctions.Add("PI");
        //            sqlFunctions.Add("POWER");
        //            sqlFunctions.Add("RADIANS");
        //            sqlFunctions.Add("RAND");
        //            sqlFunctions.Add("ROUND");
        //            sqlFunctions.Add("SIGN");
        //            sqlFunctions.Add("SIN");
        //            sqlFunctions.Add("SQRT");
        //            sqlFunctions.Add("SQUARE");
        //            sqlFunctions.Add("TAN");

        //            // String Functions
        //            sqlFunctions.Add("ASCII");
        //            sqlFunctions.Add("CHAR");
        //            sqlFunctions.Add("CHARINDEX");
        //            sqlFunctions.Add("DIFFERENCE");
        //            sqlFunctions.Add("LEFT");
        //            sqlFunctions.Add("LEN");
        //            sqlFunctions.Add("LOWER");
        //            sqlFunctions.Add("LTRIM");
        //            sqlFunctions.Add("NCHAR");
        //            sqlFunctions.Add("PATINDEX");
        //            sqlFunctions.Add("QUOTENAME");
        //            sqlFunctions.Add("REPLACE");
        //            sqlFunctions.Add("REPLICATE");
        //            sqlFunctions.Add("REVERSE");
        //            sqlFunctions.Add("RIGHT");
        //            sqlFunctions.Add("RTRIM");
        //            sqlFunctions.Add("SOUNDEX");
        //            sqlFunctions.Add("SPACE");
        //            sqlFunctions.Add("STR");
        //            sqlFunctions.Add("STUFF");
        //            sqlFunctions.Add("SUBSTRING");
        //            sqlFunctions.Add("UNICODE");
        //            sqlFunctions.Add("UPPER");
        //        }
        //        return sqlFunctions;
        //    }
        //}
    }
}
